﻿/*
	Copyright (C) 2019 Zongming Liu <1849059316@qq.com>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the Free Software Foundation, Inc.,
	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "Sprite.h"
#include "../utility/utility.h"
#include "../base/VertexType.h"
#include "../render/ProgramCache.h"
#include "../utility/MacroTool.h"
#include "../win32/ApplicationView.h"
#include "../render/CustomCommand.h"
#include "../render/ProgramStateCache.h"
#include "../render/Camera.h"
#include "../render/Texture2D.h"
#include "../node/SpriteFrame.h"
#include "../render/TextureCache.h"
#include "../base/GameWorld.h"
#include "../render/TrianglesCommand.h"
#include "../render/Material.h"
#include "../render/Texture2D.h"
#include "../render/IRender.h"
#include <memory>

namespace SkyEngine2d
{

	Sprite::Sprite(Node* parent /*= nullptr*/)
		: Node(parent)
		, m_texture(nullptr)
		, m_material(nullptr)
		, m_cmd(nullptr)
		, m_isFlipH(false)
		, m_isFlipV(false)
		, m_color(Color4f::White)
	{
		this->setAnchorPoint(Vector2(0.5f, 0.5f));
	}


	Sprite* Sprite::create(const std::shared_ptr<SpriteFrame>& sprite_frame, Node* parent /*= nullptr */)
	{
		auto s = makeNodePtr(parent);
		if (s && s->initWithSpriteFrame(sprite_frame))
		{
			if (parent)
			{
				parent->addChildren(s);
			}
			return s;
		}

		return nullptr;
	}

	Sprite* Sprite::create(const std::wstring& file_name, Node* parent /*= nullptr*/)
	{
		auto s = makeNodePtr(parent);
		if (s->initWithFile(file_name))
		{
			if (parent)
			{
				parent->addChildren(s);
			}
			return s;
		}
		return nullptr;
	}

	Sprite* Sprite::create(const std::shared_ptr<Texture2D>& texture2D, const Rect& des_rect_in_texture, Node* parent /*= nullptr */)
	{
		auto s = makeNodePtr(parent);
		if (s && s->initWithTexture(texture2D, des_rect_in_texture))
		{
			if (parent)
			{
				parent->addChildren(s);
			}
			return s;
		}
		return nullptr;
	}

	Sprite* Sprite::create(const std::wstring texture_filename, const Rect& des_rect_in_texture, Node* parent /*= nullptr */)
	{
		auto s = makeNodePtr(parent);
		if (s && s->initWithFile(texture_filename, des_rect_in_texture))
		{
			if (parent)
			{
				parent->addChildren(s);
			}
			return s;
		}
		return nullptr;
	}

	Sprite::~Sprite()
	{

	}

	bool Sprite::initWithFile(const std::wstring& file_name)
	{
		if (!Node::init())
		{
			return false;
		}

		m_texture = GameWorld::getInstance()->getTextureCache()->addImage(file_name);
		if (!m_texture)
		{
			return false;
		}

		this->setContentSize(m_texture->getContentSize());

		Rect texture_rect;
		texture_rect.setSize(m_texture->getContentSize());
		this->setTextureCoords(texture_rect, &m_quad);

		this->setVertexCoords(this->getContentSize(), &m_quad);

		this->updateRenderResource();

		return true;
	}

	bool Sprite::initWithFile(const std::wstring& file_name, const Rect& des_rect)
	{
		if (!Node::init())
		{
			return false;
		}
		m_texture = GameWorld::getInstance()->getTextureCache()->addImage(file_name);
		if (!m_texture)
		{
			return false;
		}

		this->setContentSize(des_rect.getSize());

		this->setTextureCoords(des_rect, &m_quad);

		this->setVertexCoords(this->getContentSize(), &m_quad);

		this->updateRenderResource();

		return true;
	}

	bool Sprite::initWithSpriteFrame(const std::shared_ptr<SpriteFrame>& sf)
	{
		if (!Node::init())
		{
			return false;
		}

		if (!sf)
		{
			return false;
		}

		this->setSpriteFrame(sf);

		return true;
	}

	bool Sprite::initWithTexture(const std::shared_ptr<Texture2D>& tex, const Rect& rect)
	{
		if (!Node::init())
		{
			return false;
		}

		if (!tex)
		{
			return false;
		}

		m_texture = tex;

		this->setContentSize(rect.getSize());

		this->setTextureCoords(rect, &m_quad);

		this->setVertexCoords(this->getContentSize(), &m_quad);

		this->updateRenderResource();

		return true;
	}

	void Sprite::draw(IRender* render, const Matrix4X4& transform)
	{
		DirectX::XMMATRIX mvp = DirectX::XMMatrixMultiply(transform, getCamera()->getViewProjectMat());
		m_cmd->updataMat(mvp);
		if (m_isChangeColor)
		{
			m_cmd->updataColor(m_color);
		}
		render->addRenderCommand(m_cmd.get());
	}


	void Sprite::setFlipH(bool flip /*= false*/)
	{
		if (flip == m_isFlipH)
		{
			return;
		}

		m_isFlipH = flip;

		this->updateFilp();
	}

	void Sprite::setFlipV(bool flip /*= false*/)
	{
		if (flip == m_isFlipV)
		{
			return;
		}

		m_isFlipV = flip;

		this->updateFilp();
	}

	void Sprite::setColor(const Color3& color)
	{
		m_color.x = (float)color.getR() / 255.0f;
		m_color.y = (float)color.getG() / 255.0f;
		m_color.z = (float)color.getB() / 255.0f;

		m_isChangeColor = true;
	}

	Color3 Sprite::getColor() const
	{
		Color3 c = m_color;
		return c;
	}


	void Sprite::setOpacity(float opacity)
	{
		m_color.w = opacity;
		m_isChangeColor = true;
	}


	float Sprite::getOpacity() const
	{
		return m_color.w;
	}

	void Sprite::setTexture(const std::shared_ptr<Texture2D>& texture)
	{
		if (texture == nullptr)
		{
			return;
		}

		if (m_texture == texture)
		{
			return;
		}

		m_texture = texture;

		return;
	}

	void Sprite::updateFilp()
	{
		auto sf = this->getSpriteFrame();
		this->setTextureCoords(sf->getRect(), &m_quad);
		this->updateRenderResource();
	}

	void Sprite::setSpriteFrame(const SpriteFrameSharedPtr& sprite_frame)
	{
		if (sprite_frame == nullptr)
		{
			DebugWarning("精灵帧为空");
			return;
		}

		if (m_spriteFrame != sprite_frame)
		{
			this->setContentSize(sprite_frame->getRect().getSize());
			m_spriteFrame = sprite_frame;

			auto  texture = sprite_frame->getTexture().lock();
			if (texture != nullptr)
			{
				this->setTexture(texture);

				auto r = sprite_frame->getRect();
				this->setTextureCoords(r, &m_quad);

				this->setVertexCoords(this->getContentSize(), &m_quad);

				this->updateRenderResource();
			}
			else
			{
				DebugWarning("精灵帧的纹理为null");
			}
		}
		return;
	}

	SpriteFrameSharedPtr Sprite::getSpriteFrame() const
	{
		if (m_spriteFrame && m_spriteFrame->getTexture().lock() == m_texture)
		{
			return m_spriteFrame;
		}
		//TODO 没有指定矩形区域 
		auto sf = SpriteFrame::createFromTexture(m_texture, Rect());
		if (sf)
		{
			m_spriteFrame = sf;
			return m_spriteFrame;
		}

		return nullptr;
	}

	bool Sprite::updateRenderResource()
	{

		auto s = ProgramStateCache::getInstance();

		if (m_material == nullptr)
		{
			m_material = std::make_shared<Material>(
				m_texture,
				s->getBlendState()->NonPremultiplied(),
				s->getSamplerState()->LinearClamp(),
				s->getRasterizerState()->CullCounterClockwise(),
				s->getDepthStencilState()->DepthNone(),
				ProgramCache::getInstance()->getProgramByName(ProgramCache::SHADER_POSITION_COLOR_TEXTURE)
				//ProgramCache::getInstance()->getProgramByName(ProgramCache::SHADER_POSITION_TEXTURE)
				);
		}
		else
		{
			m_material->texture = m_texture;
		}

		if (m_cmd == nullptr)
		{
			m_cmd = std::make_shared<TrianglesCommand>();
		}

		m_cmd->init(
			m_material,
			DirectX::XMMatrixMultiply(m_transformMat, getCamera()->getViewProjectMat()),
			std::vector<VertexPositionColorTexture>{
			VertexPositionColorTexture(m_quad.topRight.position, m_color, m_quad.topRight.tex),
				VertexPositionColorTexture(m_quad.bottomRight.position, m_color, m_quad.bottomRight.tex),
				VertexPositionColorTexture(m_quad.bottomLeft.position, m_color, m_quad.bottomLeft.tex),
				VertexPositionColorTexture(m_quad.topLeft.position, m_color, m_quad.topLeft.tex),
		},
			std::vector<UINT>{
				0, 1, 2,
					0, 2, 3
			}
			);

		return true;
	}

	void Sprite::setTextureCoords(const Rect& rect, V2C4T2_Quad* out_quad)
	{
		if (m_texture == nullptr)
		{
			return;
		}

		auto& textSize = m_texture->getContentSize();
		const float texWidth = textSize.width;
		const float texHeight = textSize.height;

		float rectWidth = rect.getWidth();
		float rectHeight = rect.getHeight();

		float left = 0;
		float right = 0;
		float bottom = 0;
		float top = 0;

		if (m_isFlipV)
		{
			right = rect.getLeft() / texWidth;
			left = rect.getRight() / texWidth;
		}
		else
		{
			left = rect.getLeft() / texWidth;
			right = rect.getRight() / texWidth;
		}

		if (m_isFlipH)
		{
			top = 1 - rect.getBottom() / texHeight;
			bottom = 1 - rect.getTop() / texHeight;
		}
		else
		{
			bottom = 1 - rect.getBottom() / texHeight;
			top = 1 - rect.getTop() / texHeight;
		}

		out_quad->bottomLeft.tex = Vector2(left, bottom);
		out_quad->bottomRight.tex = Vector2(right, bottom);
		out_quad->topLeft.tex = Vector2(left, top);
		out_quad->topRight.tex = Vector2(right, top);
	}


	void Sprite::setVertexCoords(const Size& sprite_size, V2C4T2_Quad* out_quad)
	{
		//左下角的坐标
		float x = 0;
		float y = 0;

		//尺寸
		float w = sprite_size.width;
		float h = sprite_size.height;

		out_quad->bottomLeft.position = Vector3(x, y, 1.0f);
		out_quad->bottomRight.position = Vector3(x + w, y, 1.0f);
		out_quad->topLeft.position = Vector3(x, y + h, 1.0f);
		out_quad->topRight.position = Vector3(x + w, y + h, 1.0f);
	}

}
